﻿using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace QQ2564874169.Core.ORM
{
    public abstract class BaseSql : Sql
    {
        private IDbConnection _conn;
        private IDbTransaction _transaction;

        protected BaseSql(IDbConnection conn)
        {
            _conn = conn;
        }

        protected abstract IDbDataParameter ToParam(string name, object value);
        protected abstract string ToPageSql(string src, PageSet page);

        private IDbCommand CreateCommand(SqlContext context)
        {
            var cmd = _conn.CreateCommand();
            cmd.CommandText = context.Command;
            cmd.CommandType = context.IsProc ? CommandType.StoredProcedure : CommandType.Text;
            if (_transaction != null)
            {
                cmd.Transaction = _transaction;
            }
            if (context.Timeout.HasValue)
            {
                cmd.CommandTimeout = context.Timeout.Value;
            }

            if (context.Param != null)
            {
                foreach (var item in context.Param)
                {
                    if (item.Value is IDataParameter dp)
                    {
                        cmd.Parameters.Add(dp);
                    }
                    else
                    {
                        cmd.Parameters.Add(ToParam(item.Key, item.Value));
                    }
                }
            }

            return cmd;
        }

        protected override void OnDispose()
        {
            if (_transaction != null)
            {
                _transaction.Rollback();
                _transaction = null;
            }
            if (_conn != null)
            {
                _conn.Close();
                _conn.Dispose();
                _conn = null;
            }
        }

        protected override SqlBeforeEventArgs OnBefore(SqlContext context)
        {
            if (_conn.State != ConnectionState.Open)
            {
                _conn.Open();
            }
            return base.OnBefore(context);
        }

        protected override void OnAfter(SqlBeforeEventArgs before)
        {
            if (!InTransaction)
            {
                _conn.Close();
            }
            base.OnAfter(before);
        }

        protected override int OnExecute(SqlContext context)
        {
            return CreateCommand(context).ExecuteNonQuery();
        }

        protected override T OnQueryOne<T>(SqlContext context)
        {
            var cmd = CreateCommand(context);

            using (var reader = cmd.ExecuteReader())
            {
                return reader.ToArray<T>(true).FirstOrDefault();
            }
        }

        protected override IEnumerable<T> OnQuery<T>(SqlContext context)
        {
            var cmd = CreateCommand(context);

            using (var reader = cmd.ExecuteReader())
            {
                return reader.ToArray<T>();
            }
        }

        protected override IMultipleReader OnMultiple(SqlContext context)
        {
            var cmd = CreateCommand(context);

            return new MultipleReader(cmd.ExecuteReader());
        }

        protected override PageResult<T> OnPager<T>(SqlContext context, PageSet pageset)
        {
            context.Command = ToPageSql(context.Command, pageset);
            var cmd = CreateCommand(context);

            using (var reader = new MultipleReader(cmd.ExecuteReader()))
            {
                var count = reader.ReadOne<int>();
                var data = reader.Read<T>();
                return new PageResult<T>(pageset, count, data);
            }
        }

        protected override void OnBeginTransaction()
        {
            if (_transaction == null)
            {
                if (_conn.State != ConnectionState.Open)
                    _conn.Open();
                _transaction = _conn.BeginTransaction();
            }
        }

        protected override void OnCommitTransaction()
        {
            if (_transaction != null)
            {
                _transaction.Commit();
                _transaction = null;
            }
        }

        protected override void OnRollbackTransaction()
        {
            if (_transaction != null)
            {
                _transaction.Rollback();
                _transaction = null;
                _conn.Close();
                _conn = null;
            }
        }
    }
}
